package concurrent.completableFuture;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.util.concurrent.*;

/**
 * @author LoneRanger
 * @version 1.0
 */
public class CompletableFutureTest {

	/**
	 * supplyAsync 创建并执行任务（有返回值）
	 * runAsync 创建并执行任务（没有返回值）
	 *
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	@Test
	public void testSupplyAsync() throws ExecutionException, InterruptedException {
		long startTime = System.currentTimeMillis();

		UserService userService = new UserService();
		MedalService medalService = new MedalService();

		//它CompletableFuture使用了默认线程池是ForkJoinPool.commonPool。
		CompletableFuture<UserInfo> userInfoCompletableFuture =
				CompletableFuture.supplyAsync(() -> userService.getUserInfo(1));

		CompletableFuture<MedalInfo> medalInfoCompletableFuture =
				CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(1));

		try {
			//模拟主线程运行
			TimeUnit.SECONDS.sleep(5);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("user:" + JSON.toJSONString(userInfoCompletableFuture.get()));
		System.out.println("medal:" + JSON.toJSONString(medalInfoCompletableFuture.get()));
		System.out.println("用时：" + (System.currentTimeMillis() - startTime));

	}

	/**
	 * thenRun
	 * thenRunAsync
	 *
	 * @throws ExecutionException
	 * @throws InterruptedException
	 */
	@Test
	public void testThenRun() throws ExecutionException, InterruptedException {
		long startTime = System.currentTimeMillis();

		ExecutorService executorService = Executors.newFixedThreadPool(2);

		UserService userService = new UserService();
		MedalService medalService = new MedalService();

		int id = 1;
		String title = "荣誉##";

		CompletableFuture<UserInfo> userInfoCompletableFuture =
				CompletableFuture.supplyAsync(() -> userService.getUserInfo(id), executorService);

		userInfoCompletableFuture.thenRunAsync(() -> {
			medalService.insertMedal(title, id, 4);
		});

		try {
			//模拟主线程运行
			System.out.println(Thread.currentThread().getName() + "-主线程执行");
			TimeUnit.SECONDS.sleep(5);
			System.out.println(Thread.currentThread().getName() + "-主线程执行成功");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("user=%s",
				JSON.toJSONString(userInfoCompletableFuture.get())));
		System.out.println(String.format("用时=%d", (System.currentTimeMillis() - startTime)));

	}

	/**
	 * 模拟任务：先根据用户id查询用户信息，获取荣誉ID，然后根据用户信息中的medalId 给用户提升荣誉等级
	 */
	@Test
	public void testThenAccept() throws ExecutionException, InterruptedException {
		long startTime = System.currentTimeMillis();

		ExecutorService executorService = Executors.newFixedThreadPool(2);

		UserService userService = new UserService();
		MedalService medalService = new MedalService();

		int userId = 1;

		CompletableFuture<UserInfo> userInfoCompletableFuture =
				CompletableFuture.supplyAsync(() -> userService.getUserInfo(userId),
						executorService);

		userInfoCompletableFuture.thenAccept((userInfo) -> {
			medalService.addMedalLevel(userInfo.getMedalId(), 1);
		});

		try {
			//模拟主线程运行
			System.out.println(Thread.currentThread().getName() + "-主线程执行");
			TimeUnit.SECONDS.sleep(5);
			System.out.println(Thread.currentThread().getName() + "-主线程执行成功");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(String.format("user=%s",
				JSON.toJSONString(userInfoCompletableFuture.get())));
		System.out.println(String.format("用时=%d", (System.currentTimeMillis() - startTime)));

	}

	/**
	 * 组合任务，AND
	 * <p>
	 * ● thenCombine：会将两个任务的执行结果作为方法入参，传递到指定方法中，且有返回值
	 * ● thenAcceptBoth: 会将两个任务的执行结果作为方法入参，传递到指定方法中，且无返回值
	 * ● runAfterBoth 不会把执行结果当做方法入参，且没有返回值。
	 */
	@Test
	public void testAnd() throws ExecutionException, InterruptedException {
		long startTime = System.currentTimeMillis();

		ExecutorService executorService = Executors.newFixedThreadPool(2);

		UserService userService = new UserService();
		MedalService medalService = new MedalService();
		SendMessageService sendMessageService = new SendMessageService();

		int userId = 1;
		int medalId = 1;

		CompletableFuture<UserInfo> userInfoCompletableFuture =
				CompletableFuture.supplyAsync(() -> userService.getUserInfo(userId),
						executorService);

		CompletableFuture<Boolean> booleanCompletableFuture =
				CompletableFuture.supplyAsync(() -> medalService.getMedalInfo(medalId),
						executorService).
						thenCombineAsync(userInfoCompletableFuture, (medalInfo, userInfo) ->
								sendMessageService.send(userInfo, medalInfo), executorService);

		try {
			//模拟主线程运行
			System.out.println(Thread.currentThread().getName() + "-主线程执行");
			TimeUnit.SECONDS.sleep(3);
			System.out.println(Thread.currentThread().getName() + "-主线程执行成功");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("发送结果=" + booleanCompletableFuture.get());
		System.out.println(String.format("用时=%d", (System.currentTimeMillis() - startTime)));

	}

}
